
#include "precompiled.h"
#include "tasks.h"

#include <core/task.h>
#include "helper.h"

#include <QUuid>
#include <QMap>

#include "plugins.h"
#include "zones.h"
#include "tasks.h"
#include "windowsmanager.h"

#include <extensions/plugin.h>
#include <extensions/application.h>
#include <extensions/context.h>
#include "script/contextimpl.h"
#include "tasks/declarativetask.h"
#include "declarativeextension.h"
#include "script/declarativeenvironmentex.h"
#include "tasks/nativetask.h"
#include "nativeplugin.h"

class TasksPrivate{
	public:
	TasksPrivate()
	{}
		~TasksPrivate() {}
		QMap<QString, TaskEntry*> tasks;
		QList<TaskEntry*> taskslist;

};

Tasks::Tasks(Opl::Core::EntityObject *parent) :
	Opl::Core::EntityObject(parent), O_PRIVATE_CREATE(Tasks)
{
}

Tasks::~Tasks()
{
	O_PRIVATE_DELETE;
}

Opl::Core::Task* Tasks::startFile(const QString& fn)
{
	return start(QUrl::fromLocalFile(fn));
}

Opl::Core::Task* Tasks::start(const QUrl& url)
{
	O_D(Tasks);
	if (!url.isValid())
	{
		LI.Error() << "Task Manager: Url is not valid:" << url.toString();
		return 0;
	}

	QString localFile = url.toLocalFile();
	if (!QFileInfo(localFile).isReadable())
	{
		LI.Error() << "Task Manager:invalid File:" << localFile;
		return 0;
	}

	//TODO:This is fucked up, it should be corrected
	Optra::Extensions::Plugin* extension = Plugins::fromFile(localFile);
	DeclarativeExtension* dext = qobject_cast<DeclarativeExtension*>(extension);
	if (dext != 0)
	{
		if (extension->type() == Optra::Extensions::Plugin::application)
		{
			dext->registerObject("tasks", Engine::instance()->tasks());
			dext->registerObject("zones", Engine::instance()->zones());
			DeclarativeTask* task = new DeclarativeTask(dext, url);
			if (!task->isValid())
				return 0;
			start(task, true, dext->name());
			return task;
		}
	}

	NativePlugin* next = qobject_cast<NativePlugin*>(extension);
	if (next != 0)
	{
		if (extension->type() == Optra::Extensions::Plugin::application)
		{
			next->registerObject("tasks", Engine::instance()->tasks());
			next->registerObject("zones", Engine::instance()->zones());
			NativeTask* task = new NativeTask(next, url);
			if (!task->isValid())
				return 0;
			start(task, true, next->name());
			return task;
		}
	}

	LI.Error() << "Unsupported application type";
	return 0;
}

bool Tasks::start(Opl::Core::Task* task, bool autodelete, QString name/* = QString()*/)
{
	O_D(Tasks);
	Q_ASSERT(task);

	if (name.isEmpty())
		name = task->unique().toString();
	TaskEntry* te = new TaskEntry(name, task, QUrl(), autodelete);
	d->tasks.insert(task->unique(), te);
	d->taskslist.append(te);
	connect(task, SIGNAL(done()), this, SLOT(taskDone()));
	LI.Debug() << "Starting Task: " << task->toString();
	task->start();
	emit tasksChanged();
	emit taskStarted(task->unique().toString());
	return true;
}

QDeclarativeListProperty<TaskEntry> Tasks::tasks()
{
	O_D(Tasks);
	return QDeclarativeListProperty<TaskEntry>(this, d->taskslist);
}

bool Tasks::start(Opl::Core::Task* task, QString name/* = QString()*/)
{
	return start(task, true, name);
}

void Tasks::taskDone()
{
	Opl::Core::Task* t = (Opl::Core::Task*)sender();

	LI.Debug() << "Task done :" << t->unique().toString();

	O_D(Tasks);
	TaskEntry* te = d->tasks.value(t->unique());
	d->tasks.remove(t->unique());
	d->taskslist.removeOne(te);
	emit taskFinished(t->unique().toString());
	if (te->autodelete())
	{
		LI.Trace() << "Queued to delete";
		t->deleteLater();
		te->deleteLater();
	}	
}

void Tasks::stop(const QString& uuid)
{
	O_D(Tasks);
	QString uid = uuid;
	if (!d->tasks.contains(uid))
	{
		LI.Warning() << "Task not found:" << uid;
		return;
	}
	TaskEntry* entry = d->tasks[uid];
	LI.Debug() << "Stoping task:" << uid;
	LI.Trace() << entry->task()->toString();
	entry->task()->stop();
	//QTimer::singleShot(0, entry->task(), SLOT(stop()));
}

void Tasks::stopAll()
{
	O_D(Tasks);
	QList<TaskEntry*> keys = d->taskslist;
	foreach(TaskEntry* key, keys)
	{
		stop(key->unique());
	}
}
